"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
exports.nextTick = nextTick;

var _ahooks = require("ahooks");

var _react = _interopRequireWildcard(require("react"));

var _xterm = require("xterm");

var _xtermAddonFit = require("xterm-addon-fit");

var _tools = require("./tools");

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function nextTick(fun) {
  Promise.resolve().then(fun);
}

var terminalTitleTemplate = '$: ';
var defaultPreLog = '\r\n*** COMMAND START ***\r\n';

var defaultOptions = disableStdin => ({
  rendererType: 'canvas',
  // 渲染类型
  theme: {
    foreground: '#333',
    // 字体
    background: '#fff',
    // 背景色
    cursor: '#333',
    selection: '#333'
  },
  disableStdin,
  cursorWidth: disableStdin ? undefined : 5,
  fontSize: 12,
  cursorBlink: !disableStdin,
  // 光标闪烁
  cursorStyle: 'bar',
  // 光标样式 'block' | 'underline' | 'bar'
  scrollback: 100,
  // 当行的滚动超过初始值时保留的行视窗，越大回滚能看的内容越多，
  screenReaderMode: true
});

var XtermTerminal = _ref => {
  var {
    className,
    style,
    height = 0,
    width,
    onCommand,
    onClear,
    onCopy,
    onPaste,
    disableStdin,
    preLog = defaultPreLog,
    command,
    onLoad,
    lineTitle = terminalTitleTemplate,
    colorRules,
    options
  } = _ref;
  var terminalWrapperRef = (0, _react.useRef)(null); // 终端中输入的缓存

  var terminalCmdRef = (0, _react.useRef)({
    currentLineData: '',
    historyLineData: [],
    last: 0
  });
  var [wrapperStyle, setWrapperStyle] = (0, _react.useState)({});
  var [state, setState] = (0, _ahooks.useSetState)({
    fitAddon: undefined,
    term: undefined
  }); // api中使用的

  var writeln = (0, _ahooks.useMemoizedFn)(label => {
    var {
      term
    } = state;
    term === null || term === void 0 ? void 0 : term.write('\r\n');
    term === null || term === void 0 ? void 0 : term.write(lineTitle);

    if (!label || typeof label === 'string') {
      term === null || term === void 0 ? void 0 : term.write((0, _tools.getTextWithColor)(label, colorRules));
      terminalCmdRef.current.currentLineData = label || '';
    } else {
      var {
        text,
        color
      } = label;
      term === null || term === void 0 ? void 0 : term.write("".concat(_tools.ColorEnum[color]).concat(text).concat(_tools.ColorEnum.RESET));
      terminalCmdRef.current.currentLineData = text || '';
    }
  }); // api中使用

  var write = (0, _ahooks.useMemoizedFn)(label => {
    var {
      term
    } = state;

    if (!label || typeof label === 'string') {
      if ((label || '').includes('\n')) {
        term === null || term === void 0 ? void 0 : term.write(label || '');
        term === null || term === void 0 ? void 0 : term.write(lineTitle);
        terminalCmdRef.current.currentLineData = '';
      } else {
        term === null || term === void 0 ? void 0 : term.write((0, _tools.getTextWithColor)(label, colorRules));
        terminalCmdRef.current.currentLineData += label || '';
      }
    } else {
      var {
        text,
        color
      } = label;

      if ((text || '').includes('\n')) {
        term === null || term === void 0 ? void 0 : term.write(text || '');
        term === null || term === void 0 ? void 0 : term.write(lineTitle);
        terminalCmdRef.current.currentLineData = '';
      } else {
        term === null || term === void 0 ? void 0 : term.write("".concat(_tools.ColorEnum[color]).concat(text).concat(_tools.ColorEnum.RESET));
        terminalCmdRef.current.currentLineData += text || '';
      }
    }
  }); // 清理日志

  var clearLog = (0, _ahooks.useMemoizedFn)(() => {
    var {
      term
    } = state;
    var cols = terminalCmdRef.current.currentLineData.length + lineTitle.length;

    while (cols > 0) {
      term === null || term === void 0 ? void 0 : term.write('\b \b');
      cols -= 1;
    }

    term === null || term === void 0 ? void 0 : term.clear();
    term === null || term === void 0 ? void 0 : term.writeln(preLog);
    term === null || term === void 0 ? void 0 : term.write(lineTitle);

    if (command) {
      terminalCmdRef.current.currentLineData = command || '';
      term === null || term === void 0 ? void 0 : term.writeln(command || '');
    } else {
      terminalCmdRef.current.currentLineData = '';
    }

    onClear && onClear();
  }); // 重置终端尺寸

  var resizeTerm = (0, _ahooks.useMemoizedFn)(() => {
    var {
      fitAddon,
      term
    } = state;

    try {
      fitAddon === null || fitAddon === void 0 ? void 0 : fitAddon.fit();
      term === null || term === void 0 ? void 0 : term.scrollToBottom();
    } catch (e) {
      console.log(e);
    }
  }); // 终端组件api

  var terminalApi = {
    clearLog,
    writeln,
    write,
    resize: resizeTerm,
    getCommand: (0, _ahooks.useMemoizedFn)(() => terminalCmdRef.current.currentLineData)
  }; // 终端输入的字符接收

  var onTermDataChange = (0, _ahooks.useMemoizedFn)(key => {
    var term = state.term;

    if (key === '\u001b[A') {
      // up键的时候
      var len = 0;

      if (terminalCmdRef.current.historyLineData.length > 0) {
        len = terminalCmdRef.current.historyLineData.length + 1;
      }

      if (terminalCmdRef.current.last < len && terminalCmdRef.current.last > 0) {
        // 当前行有数据的时候进行删除掉在进行渲染上存储的历史数据
        for (var i = 0; i < terminalCmdRef.current.currentLineData.length; i++) {
          if (term._core.buffer.x > lineTitle.length + 1) {
            term.write('\b \b');
          }
        }

        var text = terminalCmdRef.current.historyLineData[terminalCmdRef.current.last - 1];
        term.write(text); // 重点，一定要记住存储当前行命令保证下次up或down时不会光标错乱覆盖终端提示符

        terminalCmdRef.current.currentLineData = text;
        terminalCmdRef.current.last--;
      }
    } else if (key === '\u001b[B') {
      // down键
      var lent = 0;

      if (terminalCmdRef.current.historyLineData.length > 0) {
        lent = terminalCmdRef.current.historyLineData.length - 1;
      }

      if (terminalCmdRef.current.last < lent && terminalCmdRef.current.last > -1) {
        for (var _i = 0; _i < terminalCmdRef.current.currentLineData.length; _i++) {
          if (term._core.buffer.x > lineTitle.length + 1) {
            term.write('\b \b');
          }
        }

        var _text = terminalCmdRef.current.historyLineData[terminalCmdRef.current.last + 1];
        term.write(_text);
        terminalCmdRef.current.currentLineData = _text;
        terminalCmdRef.current.last++;
      }
    } else if (key.charCodeAt(0) === 13) {
      // enter键
      // 将行数据进行添加进去
      if (terminalCmdRef.current.currentLineData !== '') {
        // 将当前行数据传入历史命令数组中存储
        terminalCmdRef.current.historyLineData.push(terminalCmdRef.current.currentLineData); // 定义当前行命令在整个数组中的位置

        terminalCmdRef.current.last = terminalCmdRef.current.historyLineData.length;
      } // 当输入clear时清空终端内容


      if (terminalCmdRef.current.currentLineData === 'clear') {
        clearLog();
      } else if (terminalCmdRef.current.currentLineData !== '') {
        onCommand && onCommand(terminalCmdRef.current.currentLineData);
      } else {
        term.write("\r\n".concat(lineTitle));
      } // 在这可以进行发起请求将整行数据传入
      // 清空当前行数据


      terminalCmdRef.current.currentLineData = '';
    } else if (key.charCodeAt(0) === 127) {
      // 删除键--》当前行偏移量x大于终端提示符所占位置时进行删除
      if (term._core.buffer.x > lineTitle.length) {
        terminalCmdRef.current.currentLineData = terminalCmdRef.current.currentLineData.slice(0, -1);
        term.write('\b \b');
      }
    } else {
      // 啥也不做的时候就直接输入
      terminalCmdRef.current.currentLineData += key;
      term.write(key);
    }
  });
  /**
   *  自定义键盘输入事件
   * 解决问题：
   * 1、禁用光标左右移动，以免造成命令错乱
   * 2、重写复制粘贴功能
   */

  var customKeyEventHandler = (0, _ahooks.useMemoizedFn)(event => {
    var {
      type,
      ctrlKey,
      metaKey,
      keyCode
    } = event; // 禁用左右移动 <- 37 ->39

    if (keyCode === 37 || keyCode === 39) {
      return false;
    } // v 86 c 67


    if (type === 'keydown') {
      // 复制
      var ctrlC = ctrlKey && keyCode === 67;
      var metaC = metaKey && keyCode === 67;

      if (ctrlC || metaC) {
        var _state$term;

        var selection = (_state$term = state.term) === null || _state$term === void 0 ? void 0 : _state$term.getSelection();

        if (selection && onCopy) {
          _asyncToGenerator(function* () {
            return yield onCopy(selection);
          })();
        }

        return false;
      } // 粘贴


      var ctrlV = ctrlKey && keyCode === 86;
      var metaV = metaKey && keyCode === 86;

      if (ctrlV || metaV) {
        if (onPaste) {
          _asyncToGenerator(function* () {
            var _state$term2;

            var text = yield onPaste();
            terminalCmdRef.current.currentLineData += text || '';
            (_state$term2 = state.term) === null || _state$term2 === void 0 ? void 0 : _state$term2.write(text || '');
          })();
        }

        return false;
      }
    }

    return true;
  }); // 键盘事件响应注册

  var registerKeyActions = term => {
    // 使其能够输入汉字
    term.onData(onTermDataChange);
    term.attachCustomKeyEventHandler(customKeyEventHandler);
  };

  var initTerminal = () => {
    var element = terminalWrapperRef.current;
    if (!element) return; // 设置了cols或者fitAddon.fit(); 当一行字符超过80个过会替换现在的内容，否则换行

    var term = new _xterm.Terminal(_objectSpread(_objectSpread({}, defaultOptions(disableStdin)), options || {}));
    var fitAddon = new _xtermAddonFit.FitAddon();
    term.loadAddon(fitAddon);
    term.open(element); // 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸)，初始化的时候宽高都是对的

    term.focus();
    if (preLog) term.write("".concat(preLog, "\r\n"));
    var nterm = term;
    registerKeyActions(nterm);
    if (lineTitle) term.write("".concat(lineTitle));

    if (command) {
      terminalCmdRef.current.currentLineData = command || '';
      term.writeln(command || '');
    }

    onLoad && onLoad(nterm, terminalApi);
    setState({
      term: nterm,
      fitAddon
    });
    nextTick(resizeTerm);
  }; // 初始化样式


  (0, _react.useEffect)(() => {
    setWrapperStyle(_objectSpread(_objectSpread({}, style || {}), {}, {
      height,
      width
    }));
  }, [height, width, style]);
  (0, _react.useEffect)(() => {
    resizeTerm();
  }, [height, width]); // 初始化终端

  (0, _react.useEffect)(() => {
    initTerminal();
    return () => {
      var _state$term3;

      (_state$term3 = state.term) === null || _state$term3 === void 0 ? void 0 : _state$term3.dispose();
    };
  }, []);
  return /*#__PURE__*/_react.default.createElement("div", {
    className: "xterm-terminal-wrapper ".concat(className || ''),
    style: wrapperStyle,
    ref: terminalWrapperRef
  });
};

var _default = /*#__PURE__*/_react.default.memo(XtermTerminal);

exports.default = _default;